package cn.iocoder.yudao.module.lowcode.core.service.impl;

import cn.iocoder.yudao.framework.common.pojo.PageResult;
import cn.iocoder.yudao.framework.common.util.object.BeanUtils;
import cn.iocoder.yudao.module.lowcode.controller.admin.script.vo.GroovyScriptCreateReqVO;
import cn.iocoder.yudao.module.lowcode.controller.admin.script.vo.GroovyScriptPageReqVO;
import cn.iocoder.yudao.module.lowcode.controller.admin.script.vo.GroovyScriptUpdateReqVO;
import cn.iocoder.yudao.module.lowcode.core.cache.GroovyScriptCacheManager;
import cn.iocoder.yudao.module.lowcode.core.script.GroovyScriptExecutionContext;
import cn.iocoder.yudao.module.lowcode.core.security.GroovyScriptSecurityManager;
import cn.iocoder.yudao.module.lowcode.core.service.GroovyScriptService;
import cn.iocoder.yudao.module.lowcode.dal.dataobject.script.GroovyScriptDO;
import cn.iocoder.yudao.module.lowcode.dal.mysql.script.GroovyScriptMapper;
import cn.iocoder.yudao.module.lowcode.enums.GroovyScriptStatusEnum;
import groovy.lang.GroovyClassLoader;
import groovy.lang.GroovyCodeSource;
import groovy.lang.GroovyShell;
import groovy.lang.Script;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.validation.annotation.Validated;

import javax.annotation.Resource;
import javax.validation.Valid;
import java.util.Collection;
import java.util.Collections;
import java.util.List;

import static cn.iocoder.yudao.framework.common.exception.util.ServiceExceptionUtil.exception;
import static cn.iocoder.yudao.module.lowcode.enums.ErrorCodeConstants.*;

/**
 * Groovy 脚本 Service 实现类
 *
 * @author 芋道源码
 */
@Service
@Validated
@Slf4j
public class GroovyScriptServiceImpl implements GroovyScriptService {

    @Resource
    private GroovyScriptMapper groovyScriptMapper;

    @Resource
    private GroovyScriptCacheManager groovyScriptCacheManager;

    @Resource
    private GroovyScriptSecurityManager groovyScriptSecurityManager;

    @Override
    @Transactional
    public Long createGroovyScript(GroovyScriptCreateReqVO createReqVO) {
        // 校验脚本名称唯一性
        validateGroovyScriptNameUnique(null, createReqVO.getScriptName());
        
        // 校验脚本安全性
        GroovyScriptSecurityManager.ScriptSecurityResult securityResult = 
            groovyScriptSecurityManager.checkScriptSecurity(createReqVO.getScriptContent());
        if (!securityResult.isSafe()) {
            throw exception(GROOVY_SCRIPT_SECURITY_VIOLATION, securityResult.getViolationMessages());
        }

        // 校验脚本语法
        ScriptValidationResult validationResult = validateScript(createReqVO.getScriptContent());
        if (!validationResult.isValid()) {
            throw exception(GROOVY_SCRIPT_INVALID_SYNTAX, validationResult.getErrorMessage());
        }

        // 插入
        GroovyScriptDO groovyScript = BeanUtils.toBean(createReqVO, GroovyScriptDO.class);
        groovyScript.setVersion(1);
        groovyScript.setActive(true);
        groovyScript.setExecuteCount(0L);
        groovyScriptMapper.insert(groovyScript);

        // 清除缓存
        groovyScriptCacheManager.evictScript(groovyScript.getScriptName());
        
        return groovyScript.getId();
    }

    @Override
    @Transactional
    public void updateGroovyScript(GroovyScriptUpdateReqVO updateReqVO) {
        // 校验存在
        GroovyScriptDO existGroovyScript = validateGroovyScriptExists(updateReqVO.getId());
        
        // 校验脚本名称唯一性
        validateGroovyScriptNameUnique(updateReqVO.getId(), updateReqVO.getScriptName());
        
        // 校验脚本安全性和语法
        if (updateReqVO.getScriptContent() != null) {
            // 安全性检查
            GroovyScriptSecurityManager.ScriptSecurityResult securityResult = 
                groovyScriptSecurityManager.checkScriptSecurity(updateReqVO.getScriptContent());
            if (!securityResult.isSafe()) {
                throw exception(GROOVY_SCRIPT_SECURITY_VIOLATION, securityResult.getViolationMessages());
            }
            
            // 语法检查
            ScriptValidationResult validationResult = validateScript(updateReqVO.getScriptContent());
            if (!validationResult.isValid()) {
                throw exception(GROOVY_SCRIPT_INVALID_SYNTAX, validationResult.getErrorMessage());
            }
        }

        // 更新
        GroovyScriptDO updateObj = BeanUtils.toBean(updateReqVO, GroovyScriptDO.class);
        
        // 如果内容发生变化，增加版本号
        if (updateReqVO.getScriptContent() != null && 
            !updateReqVO.getScriptContent().equals(existGroovyScript.getScriptContent())) {
            updateObj.setVersion(existGroovyScript.getVersion() + 1);
        }
        
        groovyScriptMapper.updateById(updateObj);

        // 清除缓存
        groovyScriptCacheManager.evictScript(existGroovyScript.getScriptName());
    }

    @Override
    @Transactional
    public void deleteGroovyScript(Long id) {
        // 校验存在
        GroovyScriptDO groovyScript = validateGroovyScriptExists(id);
        
        // 删除
        groovyScriptMapper.deleteById(id);

        // 清除缓存
        groovyScriptCacheManager.evictScript(groovyScript.getScriptName());
    }

    private GroovyScriptDO validateGroovyScriptExists(Long id) {
        GroovyScriptDO groovyScript = groovyScriptMapper.selectById(id);
        if (groovyScript == null) {
            throw exception(GROOVY_SCRIPT_NOT_EXISTS);
        }
        return groovyScript;
    }

    private void validateGroovyScriptNameUnique(Long id, String scriptName) {
        GroovyScriptDO groovyScript = groovyScriptMapper.selectByScriptName(scriptName);
        if (groovyScript == null) {
            return;
        }
        if (id == null || !id.equals(groovyScript.getId())) {
            throw exception(GROOVY_SCRIPT_NAME_DUPLICATE);
        }
    }

    @Override
    public GroovyScriptDO getGroovyScript(Long id) {
        return groovyScriptMapper.selectById(id);
    }

    @Override
    public List<GroovyScriptDO> getGroovyScriptList(Collection<Long> ids) {
        return groovyScriptMapper.selectBatchIds(ids);
    }

    @Override
    public PageResult<GroovyScriptDO> getGroovyScriptPage(GroovyScriptPageReqVO pageReqVO) {
        return groovyScriptMapper.selectPage(pageReqVO);
    }

    @Override
    public GroovyScriptDO getGroovyScriptByName(String scriptName) {
        return groovyScriptMapper.selectByScriptName(scriptName);
    }

    @Override
    public List<GroovyScriptDO> getGroovyScriptsByCategory(String category) {
        return groovyScriptMapper.selectByCategory(category);
    }

    @Override
    public List<GroovyScriptDO> getActiveGroovyScripts() {
        return groovyScriptMapper.selectActiveScripts();
    }

    @Override
    public Object executeScript(String scriptName, GroovyScriptExecutionContext context) {
        try {
            // 校验脚本存在且启用
            GroovyScriptDO scriptDO = groovyScriptMapper.selectByScriptName(scriptName);
            if (scriptDO == null) {
                throw exception(GROOVY_SCRIPT_NOT_EXISTS);
            }
            
            if (!GroovyScriptStatusEnum.ENABLED.getStatus().equals(scriptDO.getStatus())) {
                throw exception(GROOVY_SCRIPT_PERMISSION_DENIED, "脚本未启用");
            }
            
            // 执行脚本
            return groovyScriptCacheManager.executeScript(scriptName, context);
            
        } catch (Exception e) {
            log.error("[GroovyScriptServiceImpl][executeScript] 执行脚本失败: {}", scriptName, e);
            
            // 构建详细的错误信息用于前端调试
            StringBuilder errorBuilder = new StringBuilder();
            errorBuilder.append("脚本执行失败");
            
            // 添加异常类型
            errorBuilder.append(" [").append(e.getClass().getSimpleName()).append("]");
            
            // 添加主要错误信息
            if (e.getMessage() != null) {
                errorBuilder.append(": ").append(e.getMessage());
            }
            
            // 添加根本原因信息
            Throwable rootCause = e;
            while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
                rootCause = rootCause.getCause();
            }
            
            if (rootCause != e && rootCause.getMessage() != null) {
                errorBuilder.append(" | 根本原因: ").append(rootCause.getMessage());
            }
            
            throw exception(GROOVY_SCRIPT_EXECUTION_FAILED, errorBuilder.toString());
        }
    }

    @Override
    public ScriptValidationResult validateScript(String scriptContent) {
        return new ScriptValidationResultImpl(scriptContent);
    }

    @Override
    public Object testScript(String scriptContent, GroovyScriptExecutionContext context) {
        log.debug("[GroovyScriptServiceImpl][testScript] 开始测试脚本，内容长度: {}, 参数: {}", 
            scriptContent.length(), context.getParameters());
        
        try {
            // 验证脚本安全性
            GroovyScriptSecurityManager.ScriptSecurityResult securityResult = 
                groovyScriptSecurityManager.checkScriptSecurity(scriptContent);
            if (!securityResult.isSafe()) {
                log.warn("[GroovyScriptServiceImpl][testScript] 脚本安全检查失败: {}", securityResult.getViolationMessages());
                throw exception(GROOVY_SCRIPT_SECURITY_VIOLATION, securityResult.getViolationMessages());
            }
            
            // 验证脚本语法
            ScriptValidationResult validationResult = validateScript(scriptContent);
            if (!validationResult.isValid()) {
                log.warn("[GroovyScriptServiceImpl][testScript] 脚本语法检查失败: {}", validationResult.getErrorMessage());
                throw exception(GROOVY_SCRIPT_INVALID_SYNTAX, validationResult.getErrorMessage());
            }
            
            log.debug("[GroovyScriptServiceImpl][testScript] 安全性和语法检查通过，开始编译执行");
            
            // 编译并执行测试脚本
            // 使用当前线程的类加载器，确保能访问到Groovy扩展模块
            GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());
            try {
                GroovyCodeSource codeSource = new GroovyCodeSource(
                    scriptContent,
                    "TestScript.groovy", 
                    GroovyShell.DEFAULT_CODE_BASE
                );
                
                log.debug("[GroovyScriptServiceImpl][testScript] 开始编译脚本类");
                Class<? extends Script> scriptClass = classLoader.parseClass(codeSource);
                
                log.debug("[GroovyScriptServiceImpl][testScript] 脚本编译成功，创建实例");
                Script script = scriptClass.getDeclaredConstructor().newInstance();
                script.setBinding(context.createBinding());
                
                log.debug("[GroovyScriptServiceImpl][testScript] 开始执行脚本");
                Object result = script.run();
                
                log.debug("[GroovyScriptServiceImpl][testScript] 脚本执行成功，结果类型: {}", 
                    result != null ? result.getClass().getSimpleName() : "null");
                
                return result;
                
            } finally {
                classLoader.close();
            }
            
        } catch (cn.iocoder.yudao.framework.common.exception.ServiceException e) {
            // 重新抛出业务异常，不包装
            throw e;
        } catch (Exception e) {
            log.error("[GroovyScriptServiceImpl][testScript] 测试脚本失败，脚本内容: {}", scriptContent, e);
            
            // 构建详细的错误信息用于前端调试
            StringBuilder errorBuilder = new StringBuilder();
            errorBuilder.append("脚本执行失败");
            
            // 添加异常类型
            errorBuilder.append(" [").append(e.getClass().getSimpleName()).append("]");
            
            // 添加主要错误信息
            if (e.getMessage() != null) {
                errorBuilder.append(": ").append(e.getMessage());
            }
            
            // 添加根本原因信息
            Throwable rootCause = e;
            while (rootCause.getCause() != null && rootCause.getCause() != rootCause) {
                rootCause = rootCause.getCause();
            }
            
            if (rootCause != e && rootCause.getMessage() != null) {
                errorBuilder.append(" | 根本原因: ").append(rootCause.getMessage());
            }
            
            // 如果是编译错误，添加更多调试信息
            if (e instanceof groovy.lang.MissingPropertyException) {
                groovy.lang.MissingPropertyException mpe = (groovy.lang.MissingPropertyException) e;
                errorBuilder.append(" | 提示: 检查变量名是否正确，可用变量: params, parameters, userId, username 等");
            }
            
            String errorMessage = errorBuilder.toString();
            
            // 记录详细错误用于后端调试
            log.error("[GroovyScriptServiceImpl][testScript] 详细错误信息: {}", errorMessage);
            
            throw exception(GROOVY_SCRIPT_EXECUTION_FAILED, errorMessage);
        }
    }

    @Override
    public void evictScriptCache(String scriptName) {
        groovyScriptCacheManager.evictScript(scriptName);
    }

    @Override
    public void evictAllScriptCache() {
        groovyScriptCacheManager.evictAll();
    }

    /**
     * 脚本验证结果实现类
     */
    private static class ScriptValidationResultImpl implements ScriptValidationResult {
        private final boolean valid;
        private final String errorMessage;
        private final List<String> warnings;

        public ScriptValidationResultImpl(String scriptContent) {
            boolean tempValid;
            String tempErrorMessage;
            List<String> tempWarnings;
            
            GroovyClassLoader classLoader = new GroovyClassLoader(Thread.currentThread().getContextClassLoader());
            try {
                classLoader.parseClass(scriptContent);
                tempValid = true;
                tempErrorMessage = null;
                tempWarnings = Collections.emptyList();
            } catch (Exception e) {
                tempValid = false;
                tempErrorMessage = e.getMessage();
                tempWarnings = Collections.emptyList();
            } finally {
                try {
                    classLoader.close();
                } catch (Exception ignored) {
                }
            }
            
            this.valid = tempValid;
            this.errorMessage = tempErrorMessage;
            this.warnings = tempWarnings;
        }

        @Override
        public boolean isValid() {
            return valid;
        }

        @Override
        public String getErrorMessage() {
            return errorMessage;
        }

        @Override
        public List<String> getWarnings() {
            return warnings;
        }
    }
}